awscliのec2 wait instance-status-okコマンドで遭遇した思わぬ落とし穴と回避方法
AWSCLIを使う上で、waitコマンドは本当に便利です(挨拶
例えば下記のようなコマンドラインでEC2インスタンスを起動すると、プロンプトは数秒で戻ってきます。
$ aws ec2 start-instances \ --instance-ids i-08e952a8eHOGEHOGE i-020b7ddf8FUGAFUGA { "StartingInstances": [ (以下略)
もちろん、この秒数でEC2インスタンスが起動したわけではないので、
別のコマンドやマネジメントコンソール(MC)を使って状態を確認したり、wait
コマンドを使って起動するまで待ったりするわけです。
$ aws ec2 wait instance-status-ok \ --instance-ids i-08e952a8eHOGEHOGE i-020b7ddf8FUGAFUGA
この時わたしはinstance-status-ok
、つまり
「指定したインスタンスIDが 全て ステータス : OK」になるまで待ってくれる、
と思っていたのですが、実際はそうではなかったのでした。
落とし穴 : instance-status-okはrunningのインスタンスしか見てくれない
例えばいま、こういう状態になっていたとして(インスタンスID,ステータスの順に表示)、
$ aws ec2 describe-instances \ --instance-ids i-08e952a8eHOGEHOGE i-020b7ddf8FUGAFUGA \ --query Reservations[].Instances[].[InstanceId,State.Name] --output text i-020b7ddf8FUGAFUGA stopped i-08e952a8eHOGEHOGE running
wait instance-status-ok
は速攻で戻ってきてしまいます。
$ time aws ec2 wait instance-status-ok \ --instance-ids i-08e952a8eHOGEHOGE i-020b7ddf8FUGAFUGA real 0m1.227s user 0m0.464s sys 0m0.358s
実はstopped
のインスタンスは、チェックの対象から外れています。
なので一見、stopped
のインスタンスもステータス : OKと判断されているかのような挙動になってしまいます。
この挙動を理解していないと、
例えば「全てのインスタンスの起動をまって次の処理を行う」というスクリプトを組んだときに、たまたま一つインスタンスの起動に失敗したという場合で想定外のことになりそうです。
$ aws ec2 wait instance-status-ok \ --instance-ids i-08e952a8eHOGEHOGE i-020b7ddf8FUGAFUGA && \ echo "ALL OK! DO NEXT!!"
全然okじゃないですよね…
回避策 1 : 二段構え
簡単に思いつくのは、「先にinstance-running
でチェックする」という方法です。
$ aws ec2 wait instance-running \ --instance-ids i-08e952a8eHOGEHOGE i-020b7ddf8FUGAFUGA && \ aws ec2 wait instance-status-ok \ --instance-ids i-08e952a8eHOGEHOGE i-020b7ddf8FUGAFUGA && echo "ok."
これだと、まずrunning
ステータスになるまで待ってから次に進むため確実です。
わたしも最初、この方法で回避していたのですが、
ただ2回コマンドを打つ必要がある(スクリプトだと2行になる)ので、場合によっては美しくないかもしれません。
回避策 2 : --include-all-instances
この挙動をいろいろ調べているうちに、--include-all-instances
というオプションの存在に気づきました。
$ aws ec2 wait instance-status-ok help (略) --include-all-instances | --no-include-all-instances (boolean) When true , includes the health status for all instances. When false, includes the health status for running instances only. Default: false (以下略)
つまりこのオプションを指定すれば、指定したインスタンスが全て(running
か否かにかかわらず)対象になるわけです。
$ aws ec2 describe-instances \ --instance-ids i-08e952a8eHOGEHOGE i-020b7ddf8FUGAFUGA \ --query Reservations[].Instances[].[InstanceId,State.Name] --output text i-020b7ddf8FUGAFUGA stopped i-08e952a8eHOGEHOGE running
$ time aws ec2 wait instance-status-ok \ --instance-ids i-08e952a8eHOGEHOGE i-020b7ddf8FUGAFUGA real 0m1.302s user 0m0.478s sys 0m0.486s
$ time aws ec2 wait instance-status-ok \ --include-all-instances \ --instance-ids i-08e952a8eHOGEHOGE i-020b7ddf8FUGAFUGA ^C real 0m12.114s user 0m0.477s sys 0m0.440s
ちょっと分かりづらいですが、
対象の2つのインスタンスのうち片方がstopped
の場合、
1回目のaws ec2 wait
だとすぐにプロンプトが戻ってくるのですが、
2回めの--include-all-instances
を指定した方はCtrl-C
するまで戻ってきません。
もちろんCtrl-C
で中断せずに、そのまま別ターミナルやMCから起動してやれば、
起動が完了し、ステータスチェックが2/2で合格になった時点でプロンプトが戻ってきます。
ちなみに(ちょっとだけ深掘り)
御存知の通り、AWSCLIのwait
コマンドは内部では単に定期的にAPIを叩きにいって、その応答を見て待つ/終了するを判断しているだけです。
help
にも冒頭にこう書いてあります。
NAME instance-status-ok - DESCRIPTION Wait until JMESPath query InstanceStatuses[].InstanceStatus.Status returns ok for all elements when polling with describe-instance-status. It will poll every 15 seconds until a successful state has been reached. This will exit with a return code of 255 after 40 failed checks.
15秒間隔でdescribe-instance-status
を投げている、とあります。
ということはつまり、describe-instance-status
にも同じようなオプションがあるということですね。
$ aws ec2 describe-instance-status help (略) DESCRIPTION Describes the status of one or more instances. By default, only running instances are described, unless specified otherwise. (略) --include-all-instances | --no-include-all-instances (boolean) When true , includes the health status for all instances. When false, includes the health status for running instances only. Default: false (以下略)
「同じような」どころか、まったく同じオプションがありました :)
$ aws ec2 describe-instance-status \ --instance-ids i-08e952a8eHOGEHOGE i-020b7ddf8FUGAFUGA \ --query InstanceStatuses[].[InstanceId,InstanceState.Name,SystemStatus.Status,InstanceStatus.Status] [ [ "i-08e952a8eHOGEHOGE", "running", "ok", "ok" ] ] $ aws ec2 describe-instance-status \ --include-all-instances \ --instance-ids i-08e952a8eHOGEHOGE i-020b7ddf8FUGAFUGA \ --query InstanceStatuses[].[InstanceId,InstanceState.Name,SystemStatus.Status,InstanceStatus.Status] [ [ "i-020b7ddf8FUGAFUGA", "stopped", "not-applicable", "not-applicable" ], [ "i-08e952a8eHOGEHOGE", "running", "ok", "ok" ] ]